/*
**  Copyright (c) 2014 Michael Liu(michael.liu.point@gmail.com).
**
**  Project: Gateway Unified Management Platform
**  File:    dprule.c
**  Author:  Michael
**  Date:    06/24/2014
**
**  Purpose:
**    data path rules methods.
*/

/* Include files. */

#include "dpmgr.h"
#include "svcmgr.h"
#include "osdep.h"
#include "pfdep.h"

/* Macro constant definitions. */

#define DP_RULE_IPT_RULE_FILE		"/tmp/ipt.rule"

#undef IPT_FILTER_RULE_SUPPORT
#undef IPT_MANGLE_RULE_SUPPORT
#undef IPT_RAW_RULE_SUPPORT

#define IPT_NAT_RULE_SUPPORT		1

/* Type definitions. */

/* Local function declarations. */

static e_ret build_ipt_rule_file(tdp_ipt_rule_list * ipt_rules, const char *file);

/* Macro API definitions. */

/* Global variable declarations. */

e_ret dp_rule_proc_req(tdata_paths * dp, tcm_dp_req_body * req, tcm_dp_rsp_body ** rsp, int *size)
{

	*size = 0;
	*rsp = NULL;
	return (eRET_NOT_SUPPORT);
}

e_ret dp_rules_add_ipt_rule(tdata_paths * dp, unsigned char table, const char *rule, update_rule update)
{
	struct list_head *each;
	tdp_ipt_rule_entry *entry;
	tdp_ipt_rule_list *ipt_rules;

	ipt_rules = &dp->dp_rules.ipt_rule;

	list_for_each(each, &ipt_rules->tables[table]) {
		entry = list_entry(each, tdp_ipt_rule_entry, list);
		if ((entry->ipt_rule != NULL) && (strcmp(entry->ipt_rule, rule) == 0)) {
			sysmgr_dbg("dp rule: ipt nat rule[%s] already exist.\n", rule);
			return (eRET_SUCCESS);
		}
	}

	entry = malloc(sizeof(*entry));
	if (entry == NULL) {
		sysmgr_err("malloc(%d) failed, err: %d,[%s]\n", sizeof(*entry), errno, strerror(errno));
		return (eRET_NO_RESOURCE);
	}

	INIT_LIST_HEAD(&entry->list);
	entry->ipt_rule = strdup(rule);
	entry->update = update;

	list_add_tail(&entry->list, &ipt_rules->tables[table]);

	return (eRET_SUCCESS);
}

e_ret dp_rules_del_ipt_rule(tdata_paths * dp, unsigned char table, const char *rule)
{
	struct list_head *each;
	tdp_ipt_rule_entry *entry;
	tdp_ipt_rule_list *ipt_rules;

	ipt_rules = &dp->dp_rules.ipt_rule;

	list_for_each(each, &ipt_rules->tables[table]) {
		entry = list_entry(each, tdp_ipt_rule_entry, list);
		if ((entry->ipt_rule != NULL) && (strcmp(entry->ipt_rule, rule) == 0)) {
			list_del(&entry->list);
			free(entry->ipt_rule);
			free(entry);
			break;
		}
	}

	return (eRET_SUCCESS);
}

e_ret dp_rules_apply_ipt_rule(tdata_paths * dp)
{

	build_ipt_rule_file(&dp->dp_rules.ipt_rule, DP_RULE_IPT_RULE_FILE);

#ifdef IPT_FILTER_RULE_SUPPORT
	os_dep_do_system("iptables-multi", "main", "-F", "-t", "filter", NULL);
#endif /* IPT_FILTER_RULE_SUPPORT */

#ifdef IPT_NAT_RULE_SUPPORT
	os_dep_do_system("iptables-multi", "main", "-F", "-t", "nat", NULL);
#endif /* IPT_NAT_RULE_SUPPORT */

#ifdef IPT_MANGLE_RULE_SUPPORT
	os_dep_do_system("iptables-multi", "main", "-F", "-t", "mangle", NULL);
#endif /* IPT_MANGLE_RULE_SUPPORT */

#ifdef IPT_RAW_RULE_SUPPORT
	os_dep_do_system("iptables-multi", "main", "-F", "-t", "raw", NULL);
#endif /* IPT_RAW_RULE_SUPPORT */

	os_dep_do_system("iptables-multi", "restore", DP_RULE_IPT_RULE_FILE, NULL);

	return (eRET_SUCCESS);
}

e_ret dpif_update_dns_addr_cfg(void *dp_rule)
{

	int rc = 0;
	int fd;
	unsigned char qis_done;
	char ipaddr[RC_IPV4_STR_MAX_SIZE];
	char address[256];

	fd = open(DNSMASQ_DNS_ADDRESS_CONF_FILE, O_CREAT | O_WRONLY | O_TRUNC | O_APPEND, 0666);
	if (fd < 0) {
		sysmgr_err("open(%s) failed, err: %d,[%s]\n", DNSMASQ_DNS_ADDRESS_CONF_FILE, errno, strerror(errno));
		return (eRET_FAILURE);
	}

	if (os_dep_get_if_ipaddr("lan", ipaddr) != eRET_SUCCESS) {
		strcpy(ipaddr, PF_DEP_DEFAULT_IP_ADDR);
	}

	rc += sprintf(&address[rc], "address=/%s/%s\n", PF_DEP_MANAGEMENT_URL, ipaddr);

	pf_dep_get_qis(&qis_done);
	if (!qis_done) {
		rc += sprintf(&address[rc], "address=/#/8.8.8.8\n");
	}

	write(fd, address, rc);
	close(fd);

	return (eRET_SUCCESS);
}

e_ret build_ipt_rule_file(tdp_ipt_rule_list * ipt_rules, const char *file)
{
	FILE *fptr;
	time_t now;
	struct list_head *each;
	tdp_ipt_rule_entry *entry;
	char rule_buf[512];

	fptr = fopen(file, "wb");
	if (fptr == NULL) {
		sysmgr_err("fopen(%s) failed, err: %d,[%s]\n", DP_RULE_IPT_RULE_FILE, errno, strerror(errno));
		return (eRET_FAILURE);
	}

	now = time(NULL);
	fprintf(fptr, "# Generated by iptables-save v1.4.10 on %s", ctime(&now));

#ifdef IPT_FILTER_RULE_SUPPORT
	fprintf(fptr, "*filter\n");
	fprintf(fptr, ":INPUT ACCEPT [0:0]\n");
	fprintf(fptr, ":FORWARD ACCEPT [0:0]\n");
	list_for_each(each, &ipt_rules->tables[eIPT_RULE_TABLE_FILTER]) {
		entry = list_entry(each, tdp_ipt_rule_entry, list);
		if (entry->ipt_rule != NULL) {
			if (entry->update != NULL) {
				entry->update(rule_buf, entry);
				if (rule_buf[0] != 0) {
					fprintf(fptr, "%s", rule_buf);
				}
			} else {
				fprintf(fptr, "%s\n", entry->ipt_rule);
			}
		}
	}
	fprintf(fptr, "COMMIT\n");
	now = time(NULL);
	fprintf(fptr, "# Completed on %s", ctime(&now));
	fflush(fptr);
#endif /* IPT_FILTER_RULE_SUPPORT */

#ifdef IPT_NAT_RULE_SUPPORT
	fprintf(fptr, "*nat\n");
	fprintf(fptr, ":PREROUTING ACCEPT [0:0]\n");
	fprintf(fptr, ":INPUT ACCEPT [0:0]\n");
	fprintf(fptr, ":OUTPUT ACCEPT [0:0]\n");
	fprintf(fptr, ":POSTROUTING ACCEPT [0:0]\n");
	list_for_each(each, &ipt_rules->tables[eIPT_RULE_TABLE_NAT]) {
		entry = list_entry(each, tdp_ipt_rule_entry, list);
		if (entry->ipt_rule != NULL) {
			if (entry->update != NULL) {
				entry->update(rule_buf, entry);
				if (rule_buf[0] != 0) {
					fprintf(fptr, "%s", rule_buf);
				}
			} else {
				fprintf(fptr, "%s\n", entry->ipt_rule);
			}
		}
	}
	fprintf(fptr, "COMMIT\n");
	now = time(NULL);
	fprintf(fptr, "# Completed on %s", ctime(&now));
	fflush(fptr);
#endif /* IPT_NAT_RULE_SUPPORT */

#ifdef IPT_MANGLE_RULE_SUPPORT
	fprintf(fptr, "*mangle\n");
	list_for_each(each, &ipt_rules->tables[eIPT_RULE_TABLE_MANGLE]) {
		entry = list_entry(each, tdp_ipt_rule_entry, list);
		if (entry->ipt_rule != NULL) {
			if (entry->update != NULL) {
				entry->update(rule_buf, entry);
				if (rule_buf[0] != 0) {
					fprintf(fptr, "%s", rule_buf);
				}
			} else {
				fprintf(fptr, "%s\n", entry->ipt_rule);
			}
		}
	}
	fprintf(fptr, "COMMIT\n");
	now = time(NULL);
	fprintf(fptr, "# Completed on %s", ctime(&now));
	fflush(fptr);
#endif /* IPT_MANGLE_RULE_SUPPORT */

#ifdef IPT_RAW_RULE_SUPPORT
	fprintf(fptr, "*raw\n");
	list_for_each(each, &ipt_rules->tables[eIPT_RULE_TABLE_RAW]) {
		entry = list_entry(each, tdp_ipt_rule_entry, list);
		if (entry->ipt_rule != NULL) {
			if (entry->update != NULL) {
				entry->update(rule_buf, entry);
				if (rule_buf[0] != 0) {
					fprintf(fptr, "%s", rule_buf);
				}
			} else {
				fprintf(fptr, "%s\n", entry->ipt_rule);
			}
		}
	}
	fprintf(fptr, "COMMIT\n");
	now = time(NULL);
	fprintf(fptr, "# Completed on %s", ctime(&now));
	fflush(fptr);
#endif /* IPT_RAW_RULE_SUPPORT */

	return (eRET_SUCCESS);

}
